import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import os  # 新增：用于创建目录

# ----------------------
# 数据加载与初步探索
# ----------------------
# 加载数据（Windows路径）
df = pd.read_csv('D:/swh05/byssss/data/flight_passengers.csv')

# 查看数据基本信息
print('数据基本信息：')
df.info()

# 查看数据集行数和列数
rows, columns = df.shape
print(f'数据集规模：{rows}行，{columns}列')

# 查看数据前几行
print('数据前几行内容：')
print(df.head())

# ----------------------
# 数据预处理
# ----------------------
# 处理数值型列的异常值
numeric_cols = df.select_dtypes(include=['int64']).columns
for col in numeric_cols:
    q1 = df[col].quantile(0.25)
    q3 = df[col].quantile(0.75)
    iqr = q3 - q1
    lower_bound = q1 - 1.5 * iqr
    upper_bound = q3 + 1.5 * iqr

    # 将异常值替换为上下边界值（ Winsorization 方法）
    df[col] = df[col].clip(lower_bound, upper_bound)
    print(f"{col} 异常值处理完成，上下边界: {lower_bound:.2f} - {upper_bound:.2f}")

# 处理分类型列的异常值
categorical_cols = df.select_dtypes(include=['object']).columns
for col in categorical_cols:
    # 假设出现频率低于0.01%的类别为异常值
    value_counts = df[col].value_counts(normalize=True)
    rare_categories = value_counts[value_counts < 0.0001].index
    if len(rare_categories) > 0:
        df[col] = df[col].apply(lambda x: 'Other' if x in rare_categories else x)
        print(f"{col} 处理了 {len(rare_categories)} 个稀有类别")

# 处理缺失值（针对 baggage 列）
if 'baggage' in df.columns:
    # 用最频繁值填充缺失值
    most_frequent = df['baggage'].mode()[0]
    df['baggage'] = df['baggage'].fillna(most_frequent)
    print(f"baggage 列缺失值已用最频繁值 '{most_frequent}' 填充")

# ----------------------
# 调整保存路径（适配Windows）
# ----------------------
# 创建保存目录（如果不存在）
output_dir = 'D:/swh05/byssss/output'  # Windows路径
os.makedirs(output_dir, exist_ok=True)  # 自动创建目录，已存在则不报错

# 保存预处理后的数据
csv_path = f'{output_dir}/flight_passengers_preprocessed.csv'
df.to_csv(csv_path, index=False)
print(f"预处理后的数据已保存至: {csv_path}")

# ----------------------
# 高级可视化分析
# ----------------------
# 设置图片清晰度和中文字体
plt.rcParams['figure.dpi'] = 300
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei', 'Heiti TC', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 1. 不同客舱等级下平均飞行时长的柱状图
plt.figure(figsize=(10, 6))
cabin_flight = df.groupby('cabin_class')['flight_duration'].mean().reset_index()
bar_plot = sns.barplot(x='cabin_class', y='flight_duration', data=cabin_flight, palette='viridis')
plt.xlabel('客舱等级')
plt.ylabel('平均飞行时长')
plt.title('不同客舱等级下的平均飞行时长')

# 添加数值标签
for i, row in cabin_flight.iterrows():
    bar_plot.text(i, row['flight_duration'] + 0.1, f'{row["flight_duration"]:.2f}',
                 ha='center', va='bottom')
plt.tight_layout()
plt.savefig(f'{output_dir}/cabin_flight_duration.png')
plt.show()

# 2. 年龄分布直方图
plt.figure(figsize=(10, 6))
sns.histplot(df['age'], bins=20, kde=True, color='skyblue', edgecolor='black')
plt.xlabel('年龄')
plt.ylabel('数量')
plt.title('乘客年龄分布')
plt.axvline(df['age'].mean(), color='red', linestyle='dashed', linewidth=1,
           label=f'平均值: {df["age"].mean():.1f}')
plt.legend()
plt.tight_layout()
plt.savefig(f'{output_dir}/age_distribution.png')
plt.show()

# 3. 旅行目的和是否常旅客的热力图
plt.figure(figsize=(10, 6))
cross_tab = pd.crosstab(df['travel_purpose'], df['frequent_flyer'], normalize='index')
sns.heatmap(cross_tab, annot=True, fmt='.2f', cmap='YlGnBu', cbar=True)
plt.xlabel('是否常旅客')
plt.ylabel('旅行目的')
plt.title('旅行目的与常旅客身份的关系')
plt.tight_layout()
plt.savefig(f'{output_dir}/travel_purpose_frequent_flyer.png')
plt.show()

# 4. 年龄与预订窗口的散点图（按旅行目的分组）
plt.figure(figsize=(12, 7))
sns.scatterplot(x='age', y='booking_window', hue='travel_purpose',
               style='travel_purpose', s=100, alpha=0.7, data=df)
plt.xlabel('年龄')
plt.ylabel('预订窗口时间')
plt.title('年龄与预订窗口时间的关系（按旅行目的分组）')
plt.legend(title='旅行目的', bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.savefig(f'{output_dir}/age_booking_window.png')
plt.show()

# 5. 付费选座情况的饼图
plt.figure(figsize=(8, 8))
paid_seat_counts = df['paid_seat'].value_counts().rename({0: '未付费', 1: '已付费'})
plt.pie(paid_seat_counts, labels=paid_seat_counts.index, autopct='%1.1f%%',
        startangle=90, colors=['lightcoral', 'lightgreen'])
plt.title('付费选座比例')
plt.axis('equal')  # 保证饼图是正圆形
plt.tight_layout()
plt.savefig(f'{output_dir}/paid_seat_pie.png')
plt.show()

print("所有可视化图表已生成并保存至目录: ", output_dir)